package com.config;

import com.filter.JwtAuthenticationTokenFilter;
import com.properties.WebProperties;
import com.security.CustomUserDetailsServiceImpl;
import com.util.AuthenticationAdaptor;
import com.util.JwtTokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String AUTHORIZATION_TOKEN = "access_token";

    public static final String AUTHORIZATION_HEADER = "Authorization";

    @Autowired
    private WebProperties webProperties;

    @Autowired
    private CustomUserDetailsServiceImpl userDetailsService;

    @Autowired
    private JwtTokenUtils jwtTokenProvider;

    @Autowired
    private AuthenticationEntryPoint unauthorizedHandler;

    @Autowired
    private AuthenticationAdaptor authenticationAdaptor;

    //提供身份认证需要的组件
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 用户身份信息获取方法，使用spring security对用户信息进行认证时，需要配合该功能使用
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 可以用来配置静态资源、白名单之类的内容
//        web.ignoring().mvcMatchers("**.js");
//        super.configure(web);
        web.ignoring().mvcMatchers(webProperties.getWhitelist().toArray(new String[0]));
        web.ignoring().mvcMatchers(webProperties.getIpWhitelistUrl().toArray(new String[0]));
        // 允许访问所有静态页面
        web.ignoring().mvcMatchers("/**.html");
        super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 实际上是添加各种各样的拦截器配置
        // 配置是有顺序的，路径会依次进行匹配，且只会匹配一次
        http
                // 提供一个默认的登陆界面
//                .formLogin()
//                .and()
                // 需要对请求进行认证
                .authorizeRequests()
//                .antMatchers("/login").permitAll()
                // 所有匹配的请求允许直接通过
//                .antMatchers("/test/**").permitAll()
                // 满足spel表达式的用户允许通过
//                .antMatchers("/hello/**").access("hasRole('ROLE')")
                // 所有的请求都需要登陆
                .anyRequest().authenticated().and()
                // custom token authorize exception handler
                .exceptionHandling()
                .authenticationEntryPoint(unauthorizedHandler).and()
                // since we use jwt, session is not necessary
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // since we use jwt, csrf is not necessary
                .csrf().disable();
        // 权限处理连接器
        http.addFilterBefore(new JwtAuthenticationTokenFilter(jwtTokenProvider, authenticationAdaptor), UsernamePasswordAuthenticationFilter.class);
        // disable cache
        http.headers().cacheControl();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
